{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "6b998a5c-45a6-4f05-9c7c-b0f3c4c74c17",
   "metadata": {},
   "source": [
    "# NVIDIA AI Research Assistant"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a0f2acb-52d9-49dd-9676-2d58715786f5",
   "metadata": {},
   "source": [
    "Ever wished you could generate podcasts from your own private data?\n",
    "ARA (AI Research Assistant) makes this possible. At its core, ARA is a sophisticated system that:\n",
    "\n",
    "- Transforms dense PDF documents into natural, engaging conversations\n",
    "- Creates AI-generated podcasts with either single-speaker or two-person formats\n",
    "- Uses cutting-edge language models (powered by LLama 3.1-70B NIM) to ensure high-quality content\n",
    "- Leverages ElevenLabs' voice synthesis for natural-sounding audio"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5af66273-f19d-4d4f-8480-31c5c03c5951",
   "metadata": {},
   "source": [
    "## Features\n",
    "What sets ARA apart is its flexible, microservice-driven architecture. Whether you're running on a single machine or a distributed cluster, ARA adapts to your needs. ARA also comes packed with modern tooling and observability features. **We've built this for you to edit and deploy on your own infrastructure with ease**:\n",
    "\n",
    "#### Observability & Monitoring\n",
    "- **Jaeger Tracing 🔍** - experience full distributed tracing built in. Watch requests flow through different services and quickly identify bottlenecks in your processing pipeline as you add your own code\n",
    "- **MinIO Object Storage 📦** - robust, S3-compatible storage for handling PDFs and generated audio content. Perfect for scaling from development to production workloads.\n",
    "- **GPU Optimized PDF processing 🔥** - choose between docling or NV-Ingest for lighting fast optimized PDF processing\n",
    "\n",
    "#### Development Experience\n",
    "- **UV Package Management 🚀** - Lightning-fast dependency management using UV, making environment setup a breeze with `make uv`\n",
    "- **Docker Compose Integration 🐳** - one command (`make all-services`) spins up the entire stack, with smart handling of GPU resources and service dependencies.\n",
    "\n",
    "#### Quality and Testing\n",
    "- **Automated Quality Checks ✨** - integrated `ruff` for Python linting and formatting, ensuring consistent code quality across contributions.\n",
    "- **End-to-End Testing 🧪** - comprehensive test suite for verifying podcast generation, from PDF ingestion to final audio output."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0f6fcc47-fb41-4e54-9d30-4d17bc483779",
   "metadata": {},
   "source": [
    "## Prerequisites"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d09678c9-8fbe-41d7-84ad-ce624bec582c",
   "metadata": {},
   "source": [
    "### Clone the repository"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a197704e-b63c-42fc-be4b-4f3fb03acfdd",
   "metadata": {},
   "outputs": [],
   "source": [
    "!git clone https://github.com/dglogo/GenerativeAIExamples.git"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "b5992623-3098-447b-ae48-9050e39dc2fb",
   "metadata": {},
   "outputs": [],
   "source": [
    "!mv GenerativeAIExamples/community/developer-blueprint/ai-research-assistant ."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "980928d4-a318-49fa-b411-85e87eb7c070",
   "metadata": {},
   "outputs": [],
   "source": [
    "!rm -rf GenerativeAIExamples/"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5cec102b-21d3-441c-a1a8-e72dbfc6c6fd",
   "metadata": {},
   "source": [
    "### Generate an API Key for NIM Endpoints\n",
    "\n",
    "This NVIDIA API Catalog key will be used to access cloud hosted models in API Catalog.\n",
    "\n",
    "You can use different model API endpoints with the same API key."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dd47fa8f-7b46-4188-aa77-73d57438058c",
   "metadata": {},
   "source": [
    "1. Navigate to **[NVIDIA API Catalog](https://build.nvidia.com/explore/discover)**."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1b707189-4848-4ebc-ab4e-d55d7084bff5",
   "metadata": {},
   "source": [
    "2. Select a model, such as llama3-8b-instruct.\n",
    "   "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4217edc7-4689-4c91-8f32-59fe32d4240b",
   "metadata": {},
   "source": [
    "3. Select an **Input** option. The following example is of a model that offers a Docker option. Not all of the models offer this option, but all include a “Get API Key” link"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9fff83e0-cfee-4c77-910b-317f087df453",
   "metadata": {},
   "source": [
    "<img src=\"https://docscontent.nvidia.com/dims4/default/d6307a8/2147483647/strip/true/crop/1920x919+0+0/resize/2880x1378!/format/webp/quality/90/?url=https%3A%2F%2Fk3-prod-nvidia-docs.s3.us-west-2.amazonaws.com%2Fbrightspot%2Fsphinx%2F00000192-bfa6-da2c-a1f2-ffbf41aa0000%2Fnim%2Flarge-language-models%2Flatest%2F_images%2Fbuild_docker_tab.png\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1931c98c-4419-44e9-85d1-edb98c40d655",
   "metadata": {},
   "source": [
    "3. Click **Get API Key**."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ed353f53-1998-413c-9a64-1bcaad83913c",
   "metadata": {},
   "source": [
    "<img src=\"https://docscontent.nvidia.com/dims4/default/c6e2096/2147483647/strip/true/crop/1920x919+0+0/resize/2880x1378!/format/webp/quality/90/?url=https%3A%2F%2Fk3-prod-nvidia-docs.s3.us-west-2.amazonaws.com%2Fbrightspot%2Fsphinx%2F00000192-bfa6-da2c-a1f2-ffbf41aa0000%2Fnim%2Flarge-language-models%2Flatest%2F_images%2Fbuild_get_api_key.png\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5764837f-217c-43db-b48b-30ce34b6daf4",
   "metadata": {},
   "source": [
    "4. Select **\"Generate Key\"**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "67e5065b-0472-4947-a4b1-be13cace84d1",
   "metadata": {},
   "source": [
    "<img src=\"https://docscontent.nvidia.com/dims4/default/e7c4057/2147483647/strip/true/crop/1920x919+0+0/resize/2880x1378!/format/webp/quality/90/?url=https%3A%2F%2Fk3-prod-nvidia-docs.s3.us-west-2.amazonaws.com%2Fbrightspot%2Fsphinx%2F00000192-bfa6-da2c-a1f2-ffbf41aa0000%2Fnim%2Flarge-language-models%2Flatest%2F_images%2Fbuild_generate_key.png\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "646b06b0-c691-4c0e-b288-19d3fa7ca109",
   "metadata": {},
   "source": [
    "5. **Copy your key** and store it in a secure place. Do not share it."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9c8e6fdc-e035-44b9-87c8-3564f00b01ad",
   "metadata": {},
   "source": [
    "<img src=\"https://docscontent.nvidia.com/dims4/default/4b0710a/2147483647/strip/true/crop/1920x919+0+0/resize/2880x1378!/format/webp/quality/90/?url=https%3A%2F%2Fk3-prod-nvidia-docs.s3.us-west-2.amazonaws.com%2Fbrightspot%2Fsphinx%2F00000192-bfa6-da2c-a1f2-ffbf41aa0000%2Fnim%2Flarge-language-models%2Flatest%2F_images%2Fbuild_copy_key.png\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d6d68974-f114-4763-badb-9a158582f2e3",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-block alert-success\">\n",
    "    <b>Tip:</b> The key begins with the letters nvapi-."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc204aa9-8a1b-4cfe-82fd-ac35a0362407",
   "metadata": {},
   "source": [
    "## Set Environment Variables\n",
    "\n",
    "his notebook requires certain environment variables to be configured. We'll help you set these up in a `.env` file.\n",
    "\n",
    "Required variables:\n",
    "- `ELEVENLABS_API_KEY`: Your ElevenLabs API key\n",
    "- `NIM_KEY`: Your NIM API key\n",
    "- `MAX_CONCURRENT_REQUESTS`: Number of concurrent requests allowed (recommended: 1 for local development)\n",
    "\n",
    "> **Note**: While production environments use the NVIDIA Eleven Labs API key which supports concurrent requests, for local development we recommend setting `MAX_CONCURRENT_REQUESTS=1` to avoid rate limiting issues. You can obtain a free testing API key from [ElevenLabs](https://elevenlabs.io).\n",
    "\n",
    "Run the code cell below to create your `.env` file. Make sure to replace the placeholder values with your actual API keys."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c6d9aba6-f362-40fb-a0a7-eaf9e6641886",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "cd ai-research-assistant/\n",
    "\n",
    "# Backup existing .env if it exists\n",
    "if [ -f .env ]; then\n",
    "    echo \"Warning: .env file already exists. Backing up to .env.backup\"\n",
    "    mv .env .env.backup\n",
    "fi\n",
    "\n",
    "# Create new .env file\n",
    "cat > .env << EOL\n",
    "ELEVENLABS_API_KEY=<ENTER-KEY>\n",
    "NIM_KEY=<ENTER_KEY>\n",
    "MAX_CONCURRENT_REQUESTS=1\n",
    "EOL\n",
    "\n",
    "echo \"Created .env file. Please edit it with your actual API keys.\"\n",
    "echo -e \"\\nCurrent .env contents:\"\n",
    "echo \"----------------------------------------\"\n",
    "cat .env"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1317ada1-3f8d-4e98-b07d-91cf2975498b",
   "metadata": {},
   "source": [
    "## Install Dependancies\n",
    "\n",
    "We use `uv` to handle the python dependancies for this environment. You can install them by simply running `make uv` in the root of the project"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "964f1c4f-22f2-40da-9fad-fbaed4db8e9c",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "%%bash\n",
    "\n",
    "source $HOME/.local/bin/env\n",
    "cd ai-research-assistant/\n",
    "\n",
    "make uv"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5aa7f3c4-d8c3-4b96-bb43-5c15c4c4918b",
   "metadata": {},
   "source": [
    "## Spin Up Blueprint\n",
    "Docker compose scripts are provided which spin up the microservices on a single node. This docker-compose yaml file will start up each microservice. This may take up to **15 minutes** to complete.\n",
    "\n",
    "In a separate terminal window, run \n",
    "\n",
    "```\n",
    "cd ai-research-assistant/\n",
    "make all-services\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30f0d682-b20b-4dca-b966-db6605d9dadf",
   "metadata": {},
   "source": [
    "You can check if the services are up by running the cells below"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ae45f128-fe7e-4f9d-99bb-b23f8fbc4b52",
   "metadata": {},
   "outputs": [],
   "source": [
    "!curl localhost:8002/health"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "284c9bc5-6b6b-471a-b122-f029b20b2fdb",
   "metadata": {},
   "source": [
    "You should get something like \n",
    "\n",
    "```\n",
    "{\"status\":\"healthy\",\"redis\":\"up\",\"services\":{\"pdf\":\"up\",\"agent\":\"up\",\"tts\":\"up\"},\"timestamp\":1733295689.5784104}\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "48cf647f-0f0b-45e2-959b-d96b013169a5",
   "metadata": {},
   "outputs": [],
   "source": [
    "!docker ps --format \"table {{{{.ID}}}}\\t{{{{.Names}}}}\\t{{{{.Status}}}}\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7d90c358-f0e9-4607-8b88-32a44ffce74e",
   "metadata": {},
   "source": [
    "This command should produce similiar output in the following format:\n",
    "\n",
    "```\n",
    "CONTAINER ID   NAMES                                   STATUS\n",
    "12050b799fd2   ai-research-assistant-pdf-api-1         Up About a minute\n",
    "14315da77827   ai-research-assistant-api-service-1     Up About a minute\n",
    "aa8489e0b3b9   ai-research-assistant-celery-worker-1   Up About a minute\n",
    "09dcb0857515   ai-research-assistant-agent-service-1   Up About a minute\n",
    "58c46d612fbf   ai-research-assistant-tts-service-1     Up About a minute\n",
    "500a43363550   ai-research-assistant-pdf-service-1     Up About a minute\n",
    "43bc1772a0aa   ai-research-assistant-minio-1           Up About a minute (healthy)\n",
    "2b6bae7f4c1d   ai-research-assistant-jaeger-1          Up About a minute (healthy)\n",
    "60e4d815d2be   ai-research-assistant-redis-1           Up About a minute\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "425ebd50",
   "metadata": {},
   "source": [
    "Note: If you are running this as a Brev launchable, you can access the the API endpoint, Jaeger UI, and the MinIO Object Storage UI by going to your running launchable on Brev, clicking `Access`, and clicking the links in the `Deployments` section. It should look like the following: "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "509931b7",
   "metadata": {},
   "source": [
    "<img src=\"https://github.com/brevdev/notebooks/raw/main/assets/ara-launchable/services.png\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2293d461-8cd1-48fb-bce7-dbf8515ea3f4",
   "metadata": {},
   "source": [
    "## Create a podcast!\n",
    "\n",
    "For this example, we'll directly call the API to generate the podcast. First we write some helper functions to interact with the API"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "61e18296-a63f-4745-9d5a-3f0148ffabe9",
   "metadata": {},
   "outputs": [],
   "source": [
    "import requests\n",
    "import json\n",
    "import time\n",
    "from typing import List\n",
    "from IPython.display import Audio\n",
    "from pathlib import Path\n",
    "\n",
    "BASE_URL = \"http://localhost:8002\"\n",
    "\n",
    "def generate_podcast(\n",
    "    target_pdf_paths: List[str], \n",
    "    name: str,\n",
    "    duration: int,\n",
    "    speaker_1_name: str,\n",
    "    context_pdf_paths: List[str] = None,\n",
    "    is_monologue: bool = False,\n",
    "    speaker_2_name: str = None,\n",
    "    guide: str = None\n",
    ") -> str:\n",
    "    \"\"\"\n",
    "    Generate a podcast using the API.\n",
    "    \n",
    "    Args:\n",
    "        target_pdf_paths: List of paths to main PDFs to analyze\n",
    "        name: Name of the podcast\n",
    "        duration: Desired duration in minutes\n",
    "        speaker_1_name: Name of the first speaker\n",
    "        context_pdf_paths: Optional list of paths to context PDFs\n",
    "        is_monologue: Whether to generate a monologue\n",
    "        speaker_2_name: Name of second speaker (required if not monologue)\n",
    "        guide: Optional guidance for the podcast structure\n",
    "    \"\"\"\n",
    "    # Handle single path inputs\n",
    "    if isinstance(target_pdf_paths, str):\n",
    "        target_pdf_paths = [target_pdf_paths]\n",
    "    if isinstance(context_pdf_paths, str):\n",
    "        context_pdf_paths = [context_pdf_paths]\n",
    "    \n",
    "    files = []\n",
    "    \n",
    "    # Add all target PDFs\n",
    "    for pdf_path in target_pdf_paths:\n",
    "        content = Path(pdf_path).read_bytes()\n",
    "        files.append(('target_files', (Path(pdf_path).name, content, 'application/pdf')))\n",
    "    \n",
    "    # Add all context PDFs if provided\n",
    "    if context_pdf_paths:\n",
    "        for pdf_path in context_pdf_paths:\n",
    "            content = Path(pdf_path).read_bytes()\n",
    "            files.append(('context_files', (Path(pdf_path).name, content, 'application/pdf')))\n",
    "    \n",
    "    # Configure voice mapping\n",
    "    voice_mapping = {\n",
    "        \"speaker-1\": \"iP95p4xoKVk53GoZ742B\" \n",
    "    }\n",
    "    if not is_monologue:\n",
    "        voice_mapping[\"speaker-2\"] = \"9BWtsMINqrJLrRacOk9x\"\n",
    "    \n",
    "    # Create parameters\n",
    "    params = {\n",
    "        \"userId\": \"test-userid\",\n",
    "        \"name\": name,\n",
    "        \"duration\": duration,\n",
    "        \"monologue\": is_monologue,\n",
    "        \"speaker_1_name\": speaker_1_name,\n",
    "        \"voice_mapping\": voice_mapping,\n",
    "        \"guide\": guide,\n",
    "        \"vdb_task\": False\n",
    "    }\n",
    "    if not is_monologue:\n",
    "        params[\"speaker_2_name\"] = speaker_2_name\n",
    "    \n",
    "    response = requests.post(\n",
    "        f\"{BASE_URL}/process_pdf\", \n",
    "        files=files,\n",
    "        data={'transcription_params': json.dumps(params)}\n",
    "    )\n",
    "    if response.status_code != 202:\n",
    "        raise Exception(f\"Failed to submit podcast generation: {response.text}\")\n",
    "    \n",
    "    return response.json()['job_id']\n",
    "\n",
    "def get_status(job_id: str) -> dict:\n",
    "    \"\"\"Get the current status of all services for a job.\"\"\"\n",
    "    response = requests.get(f\"{BASE_URL}/status/{job_id}?userId=test-userid\")\n",
    "    if response.status_code != 200:\n",
    "        raise Exception(f\"Failed to get status: {response.text}\")\n",
    "    return response.json()\n",
    "\n",
    "def wait_for_completion(job_id: str, check_interval: int = 5, initial_delay: int = 10):\n",
    "    \"\"\"\n",
    "    Poll the status endpoint until the podcast is ready.\n",
    "    Shows a simplified progress view.\n",
    "    \"\"\"\n",
    "    print(f\"Waiting {initial_delay} seconds for job to initialize...\")\n",
    "    time.sleep(initial_delay)\n",
    "    \n",
    "    last_messages = {}  # Track last message for each service to avoid duplication\n",
    "    \n",
    "    while True:\n",
    "        try:\n",
    "            statuses = get_status(job_id)\n",
    "            \n",
    "            # Check each service and only print if status changed\n",
    "            for service, status in statuses.items():\n",
    "                current_msg = status.get('message', '')\n",
    "                if current_msg != last_messages.get(service):\n",
    "                    print(f\"[{service.upper()}] {current_msg}\")\n",
    "                    last_messages[service] = current_msg\n",
    "            \n",
    "            # Check if everything is completed\n",
    "            all_completed = all(\n",
    "                status.get('status') == 'JobStatus.COMPLETED' \n",
    "                for status in statuses.values()\n",
    "            )\n",
    "            \n",
    "            if all_completed and 'tts' in statuses:\n",
    "                print(\"\\nPodcast generation completed!\")\n",
    "                return\n",
    "            \n",
    "            # Check for failures\n",
    "            for service, status in statuses.items():\n",
    "                if status.get('status') == 'JobStatus.FAILED':\n",
    "                    raise Exception(f\"Service {service} failed: {status.get('message')}\")\n",
    "            \n",
    "            time.sleep(check_interval)\n",
    "            \n",
    "        except requests.exceptions.RequestException as e:\n",
    "            if \"Job not found\" in str(e):\n",
    "                print(\"Waiting for job to start...\")\n",
    "                time.sleep(check_interval)\n",
    "                continue\n",
    "            raise\n",
    "        except Exception as e:\n",
    "            print(f\"Error: {e}\")\n",
    "            raise"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1ddfee58-4bd2-40ad-a2aa-0c10d14a3e23",
   "metadata": {},
   "source": [
    "Here we generate a monologue using various analyst reports on NVIDIAs most recent financial earnings. You can also add your own PDFs to this Jupyter Lab and point toward them below. Note that context PDFs are optional and can be used to provide additional context for the generation process. Additonally, you can provide a `guide` to help guide the generation process."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bfc3a2b6-8930-412d-8a6f-678fad2de97c",
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    print(\"Submitting podcast generation request...\")\n",
    "    job_id = generate_podcast(\n",
    "        target_pdf_paths=[\n",
    "            \"ai-research-assistant/samples/singularity.pdf\",\n",
    "        ],\n",
    "        context_pdf_paths=[\n",
    "            \"ai-research-assistant/samples/memserve.pdf\",\n",
    "            \"ai-research-assistant/samples/mooncake.pdf\"\n",
    "        ],\n",
    "        name=\"NVIDIA Earnings Analysis\",\n",
    "        duration=15,\n",
    "        speaker_1_name=\"Alex\",\n",
    "        is_monologue=True,\n",
    "        guide=\"Focus on how the future of large scale inference will evolve\"\n",
    "    )\n",
    "    print(f\"Job ID: {job_id}\")\n",
    "    wait_for_completion(job_id)\n",
    "except Exception as e:\n",
    "    print(f\"Error: {e}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a764dfff",
   "metadata": {},
   "source": [
    "You can also generate a 2 person podcast by calling the same function but setting `is_monologue=False` and providing a `speaker_2_name`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c2a24fa8-b024-4a79-9214-d129a6b8c382",
   "metadata": {},
   "outputs": [],
   "source": [
    "!curl \"localhost:8002/output/{job_id}?userId=test-userid\" --output temp_audio.mp3\n",
    "Audio(\"temp_audio.mp3\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32d8f529-09d4-43fc-8667-bfa48fd9d029",
   "metadata": {},
   "source": [
    "## Understanding Your Generated Podcast\n",
    "\n",
    "After generating your podcast, you have access to a couple valuable outputs that provide insights into the generation process and content. Here's what endpoints you can use to explore:\n",
    "\n",
    "#### 1. The Transcript\n",
    "```python\n",
    "/saved_podcast/{job_id}/transcript\"\n",
    "```\n",
    "\n",
    "The transcript provides a text version of your podcast, which is invaluable for:\n",
    "- Quick content review without audio playback\n",
    "- Creating show notes or content summaries\n",
    "- Finding and quoting specific discussion points\n",
    "- Making content searchable and referenceable\n",
    "- Ensuring accessibility of your content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f01e8f10-6ca5-4143-9883-b479006155b8",
   "metadata": {},
   "outputs": [],
   "source": [
    "!curl \"localhost:8002/saved_podcast/{job_id}/transcript?userId=test-userid\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c4180391-05e9-4d5d-a66c-1e6dc6bb3163",
   "metadata": {},
   "source": [
    "#### 2. Prompt and Generation History\n",
    "```python\n",
    "/saved_podcast/{job_id}/history\n",
    "```\n",
    "\n",
    "The history reveals the AI's thought process, showing you:\n",
    "- How the system analyzed and interpreted your PDFs\n",
    "- Key topics and themes identified\n",
    "- The structural decisions made for the conversation\n",
    "- The reasoning behind content organization\n",
    "- How different sections were prioritized and connected\n",
    "\n",
    "This is particularly useful for:\n",
    "- Understanding how the AI makes decisions\n",
    "- Improving future podcast generations\n",
    "- Verifying content accuracy and relevance\n",
    "- Fine-tuning and evals on your prompts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cbe01fc0-e111-4fe0-9530-fe549e5892d0",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "!curl \"localhost:8002/saved_podcast/{job_id}/history?userId=test-userid\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c5642285-2746-47fc-9b47-9f58d33301bb",
   "metadata": {},
   "outputs": [],
   "source": [
    "!curl \"localhost:8002/saved_podcast/{job_id}/metadata?userId=test-userid\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aaae77e9",
   "metadata": {},
   "source": [
    "## Tools for Understanding Your Generated Podcast\n",
    "\n",
    "After generating your podcast, you can explore the generation process through several tools:\n",
    "\n",
    "#### 1. Jaeger Tracing Interface\n",
    "Access Jaeger at `localhost:16686` to:\n",
    "- Visualize the complete request flow\n",
    "- Debug processing bottlenecks\n",
    "- Monitor service performance\n",
    "- Track PDF processing and audio generation stages\n",
    "\n",
    "#### 2. MinIO Object Storage\n",
    "Access MinIO at `localhost:9001` with:\n",
    "```\n",
    "Username: minioadmin\n",
    "Password: minioadmin\n",
    "```\n",
    "Here you can:\n",
    "- Browse generated audio files\n",
    "- Access intermediate processing artifacts\n",
    "- View stored PDF documents\n",
    "- Download or share content via presigned URLs\n",
    "\n",
    "#### 3. API Endpoints\n",
    "You can access the API endpoint at `localhost:8002/docs`.\n",
    "\n",
    "> **Note**: If you are running this as a Brev launchable, you can access the the API endpoint, Jaeger UI, and the MinIO Object Storage UI by going to your running launchable on Brev, clicking `Access`, and clicking the links in the `Deployments` section. It should look like the following: "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "57e9c3f1",
   "metadata": {},
   "source": [
    "<img src=\"https://github.com/brevdev/notebooks/raw/main/assets/ara-launchable/services.png\" />"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
